/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-  */
/*
 * LiberAcus
 * Copyright (C) 2012 - 2014 Massimiliano Maniscalco <max.maniscalco@gmail.com>
 * 
LiberAcus is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * LiberAcus is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _RICEVUTA_H_
#define _RICEVUTA_H_

#include <gtkmm.h>
#include <iostream>
#include <iomanip>
#include <map>
#include <list>
#include "capo.h"
#include "prestazione.h"

#define XML_FORMAT		0
#define HUMAN_FORMAT	1
#define LABEL_FORMAT		2
#define STAMPA_FORMAT	3
#define TRE_GIORNI			259200
#define LINE_COLUMNS		32

using namespace std;

class Ricevuta
{
public:
	static inline void set_numero_ricevute_emesse ( unsigned int val );
	static inline unsigned int get_last_numero_ricevuta ( void );
	static inline unsigned int generate_next_numero_ricevuta ( void );
	static inline Glib::ustring get_string_data ( time_t tempo, char formato );
	static inline time_t get_pure_date ( const time_t &tempo );
	static inline Glib::ustring get_formatted_line ( const Glib::ustring &str1, const Glib::ustring &str2, Glib::ustring::size_type line );
	
	inline Ricevuta() : data_accettazione ( time (NULL) ), numero ( 0 ), nome_cliente ( "" ),
						acconto ( 0.0 ), data_ritiro ( time (NULL) + TRE_GIORNI ), urgente ( false ), consegnato ( false )
	{}
	inline ~Ricevuta() { }
	inline Ricevuta& operator=( const Ricevuta &rcvt );
	inline bool operator<( const Ricevuta &rcvt ) const;
	inline bool operator==( const Ricevuta &rcvt ) const;
	inline void set_data_accettazione ( time_t tempo ) { data_accettazione = tempo; }
	inline void set_data_accettazione ( struct tm *tempo_ptr ) { data_accettazione = mktime ( tempo_ptr ); }
	inline time_t get_data_accettazione ( void ) const { return data_accettazione; }
	inline void set_numero ( unsigned int val ) { numero = val; }
	inline void set_numero ( const Glib::ustring &str ) { numero = atoi ( str.c_str() ); }
	inline unsigned int get_numero ( void ) const { return numero; }
	inline Glib::ustring get_numero_string ( void ) const { return Glib::ustring::format( setfill(L'0'), setw(5), numero ); }
	inline void set_nome_cliente ( const Glib::ustring &str ) { nome_cliente.assign ( str ); }
	inline Glib::ustring get_nome_cliente ( void ) const { return nome_cliente; }
	inline void set_capi ( const list<Capo> &list_cp );
	inline list<Capo>* get_capi ( void ) { return &capi; }
	inline const list<Capo>* get_const_capi ( void ) const { return &capi; }
	inline void clear_capi ( void ) { capi.clear(); }
	inline void aggiungi_capo (const Capo& cp) { capi.push_back (cp); }
	inline void importa_prestazione (const Prestazione& prst);
	inline float get_totale( void ) const;
	inline float get_totale_da_pagare ( void ) const { return get_totale() - acconto; }
	inline void set_acconto ( float val ) { acconto = val; }
	inline void set_acconto ( const Glib::ustring &str ) { acconto = atof ( str.c_str() ); }
	inline float get_acconto ( void ) const { return acconto; }
	inline void set_data_ritiro ( time_t tempo ) { data_ritiro = tempo; }
	inline void set_data_ritiro ( struct tm *tempo_ptr ) { data_ritiro = mktime ( tempo_ptr ); }
	inline time_t get_data_ritiro ( void ) const { return data_ritiro; }
	inline void set_urgente ( bool flag ) { urgente = flag; }
	inline void set_urgente ( const Glib::ustring &str );
	inline bool is_urgente ( void ) const  { return urgente; }
	inline bool is_pagato ( void ) const { return get_totale() == acconto; }
	inline void set_consegnato ( bool flag ) { consegnato = flag; }
	inline void set_consegnato ( const Glib::ustring &str );
	inline void set_lavoro_completato ( void );
	inline bool is_consegnato ( void ) const { return consegnato; }
	inline void clean_up ( void );
	inline Glib::ustring get_xml_string ( void ) const;
	inline Glib::ustring get_ricevuta_string ( bool stampa_numero ) const;
	inline Glib::ustring get_etichetta_string ( list<Capo>::const_iterator iter, bool stampa_numero ) const;

protected:

private:
	static unsigned int ricevute_emesse;
	time_t data_accettazione;
	unsigned int numero;
	Glib::ustring nome_cliente;
	list<Capo> capi;
	float acconto;
	time_t data_ritiro;
	bool urgente;
	bool consegnato;

};

// static class function

inline void Ricevuta::
set_numero_ricevute_emesse ( unsigned int val )
{
	ricevute_emesse = val;
}

inline unsigned int Ricevuta::
get_last_numero_ricevuta ( void )
{
	return ricevute_emesse;
}

inline unsigned int Ricevuta::
generate_next_numero_ricevuta ( void )
{
	ricevute_emesse++;
	return ricevute_emesse;
}

inline Glib::ustring Ricevuta::
get_string_data ( time_t tempo, char formato )
{
	Glib::ustring str;
	struct tm *data_ora;
	Glib::ustring giorni[7] = { "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab" };
	data_ora = localtime ( &tempo );
	switch ( formato )
	{
		case XML_FORMAT:
			str.append ( Glib::ustring::compose ( "%1:%2:%3 %4/%5/%6", Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_hour ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_min ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_sec ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mday ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mon + 1 ), Glib::ustring::format ( setfill(L'0'), setw(4), data_ora->tm_year + 1900 ) ) );
			break;
		case HUMAN_FORMAT:
			str.append ( Glib::ustring::compose ( "%1 %2/%3/%4  ore %5:%6", giorni[data_ora->tm_wday], Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mday ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mon + 1 ), Glib::ustring::format ( setfill(L'0'), setw(4), data_ora->tm_year + 1900 ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_hour ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_min ) ) );
			break;
		case LABEL_FORMAT:
			str.append ( Glib::ustring::compose ( "%1 %2/%3/%4", giorni[data_ora->tm_wday], Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mday ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mon + 1 ), Glib::ustring::format ( setfill(L'0'), setw(4), data_ora->tm_year + 1900 ) ) );
			break;
		case STAMPA_FORMAT:
			str.append ( Glib::ustring::compose ( "%1 %2/%3/%4 %5:%6", giorni[data_ora->tm_wday], Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mday ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mon + 1 ), Glib::ustring::format ( setfill(L'0'), setw(4), data_ora->tm_year + 1900 ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_hour ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_min ) ) );
			break;
	}
	return str;
}

inline time_t Ricevuta::
get_pure_date ( const time_t &tempo )
{
	struct tm *data = localtime(&tempo);
	data->tm_hour = 0;
	data->tm_min = 0;
	data->tm_sec = 0;
	return mktime(data);
}

inline Glib::ustring Ricevuta::
get_formatted_line ( const Glib::ustring &str1, const Glib::ustring &str2, Glib::ustring::size_type line )
{
	Glib::ustring formatted;
	if ( ( str1.size() + str2.size() ) < line )
	{
		Glib::ustring::size_type diff = line - str1.size() - str2.size();		
		Glib::ustring spazio ( diff, ' ' );
		formatted += str1 + spazio + str2 + "\n";
	}
	else
	{
		formatted += str1 + str2 + "\n";
	}
	return formatted;
}

// member function

inline Ricevuta& Ricevuta::
operator=( const Ricevuta &rcvt )
{
	if ( this != &rcvt )
	{
		data_accettazione = rcvt.data_accettazione;
		numero = rcvt.numero;
		nome_cliente = rcvt.nome_cliente;
		set_capi(rcvt.capi);
		acconto = rcvt.acconto;
		data_ritiro = rcvt.data_ritiro;
		urgente = rcvt.urgente;
		consegnato = rcvt.consegnato;
	}
	return *this;
}

inline bool Ricevuta::
operator<( const Ricevuta &rcvt ) const
{
	if ( get_pure_date (data_accettazione) < get_pure_date (rcvt.data_accettazione))
		return true;
	else if (get_pure_date (data_accettazione) == get_pure_date (rcvt.data_accettazione) && numero < rcvt.numero)
		return true;
	else
		return false;
}

inline bool Ricevuta::
operator==( const Ricevuta &rcvt ) const
{
	if ( get_pure_date (data_accettazione) == get_pure_date (rcvt.data_accettazione) && numero == rcvt.numero )
		return true;
	else
		return false;
}

inline void Ricevuta::
set_capi ( const list<Capo> &list_cp )
{
	capi.clear();
	capi.insert(capi.begin(), list_cp.begin(), list_cp.end());
}

inline void Ricevuta::
importa_prestazione (const Prestazione& prst)
{
	unsigned short quantita = prst.get_quantita ();
	if (quantita < 1)
		quantita = 1;
	for (unsigned short n = 0; n < quantita; n++)
	{
		Capo cp = Capo (prst.get_capo (), capi.size() + 1);
		if (!cp.aggiungi_prestazione (prst))
			cerr << "ERRORE! importa_prestazione():: aggiungi_prestazione() fallita" << endl;
		capi.push_back (cp);
	}
}

inline float Ricevuta::
get_totale(void) const
{
	float totale = 0.0;
	list<Capo>::const_iterator iter_capi = capi.begin();
	list<Capo>::const_iterator end_capi = capi.end();
	while (iter_capi != end_capi)
	{
		const list<Prestazione>* prestazioni = iter_capi->get_const_prestazioni ();
		list<Prestazione>::const_iterator iter_prst = prestazioni->begin();
		list<Prestazione>::const_iterator end_prst = prestazioni->end();
		while (iter_prst != end_prst)
		{
			totale += iter_prst->get_prezzo ();
			iter_prst++;
		}
		iter_capi++;
	}
	return totale;
}

inline void Ricevuta::
set_urgente ( const Glib::ustring &str )
{
	if ( str == Glib::ustring ( "true" ) || str == Glib::ustring ( "1" ) )
		urgente = true;
	else
	{
		if ( str == Glib::ustring ( "false" ) || str == Glib::ustring ( "0" ) )
			urgente = false;
		else
			cerr << "ERRORE! set_urgente():: la stringa non contiene nessun valore valido" << endl;
	}
}

inline void Ricevuta::
set_consegnato ( const Glib::ustring &str )
{
	if ( str == Glib::ustring ( "true" ) || str == Glib::ustring ( "1" ) )
		consegnato = true;
	else
	{
		if ( str == Glib::ustring ( "false" ) || str == Glib::ustring ( "0" ) )
			consegnato = false;
		else
			cerr << "ERRORE! set_consegnato():: la stringa non contiene nessun valore valido" << endl;
	}
}

inline void Ricevuta::
clean_up ( void )
{
	numero = 0;
	nome_cliente.assign( "" );
	data_accettazione = 0;
	capi.clear();
	acconto = 0.0;
	data_ritiro = 0;
	urgente = false;
	consegnato = false;
}

inline void Ricevuta::
set_lavoro_completato ( void )
{
	list<Capo>::iterator iter_capi = capi.begin();
	list<Capo>::iterator end_capi = capi.end();
	while (iter_capi != end_capi)
	{
		list<Prestazione>* prestazioni = iter_capi->get_prestazioni ();
		list<Prestazione>::iterator iter_prst = prestazioni->begin();
		list<Prestazione>::iterator end_prst = prestazioni->end();
		while (iter_prst != end_prst)
		{
			iter_prst->set_completata (true);
			iter_prst++;
		}
		iter_capi++;
	}
}

inline Glib::ustring Ricevuta::
get_xml_string ( void ) const
{
	Glib::ustring str;
	struct tm *data_ora;
	str.append ( Glib::ustring ( "<ricevuta>\n" ) );
	str.append ( Glib::ustring ( "\t<numero>" + Glib::ustring::format ( numero ) + "</numero>\n" ) );
	str.append ( Glib::ustring ( "\t<nome_cliente>" + msu::get_xml_friendly_string(nome_cliente) + "</nome_cliente>\n" ) );
	data_ora = localtime ( &data_accettazione );
	str.append ( Glib::ustring ( "\t<data_accettazione>" + Glib::ustring::compose ( "%1-%2-%3", Glib::ustring::format ( setfill(L'0'), setw(4), data_ora->tm_year + 1900 ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mon + 1 ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mday ) ) + "</data_accettazione>\n" ) );
	str.append ( Glib::ustring ( "\t<ora_accettazione>" + Glib::ustring::compose ( "%1:%2:%3", Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_hour ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_min ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_sec ) ) + "</ora_accettazione>\n" ) );
	if (capi.size() > 0)
	{
		str.append ( Glib::ustring ( "\t<capi>\n" ) );
		list<Capo>::const_iterator iter = capi.begin();
		list<Capo>::const_iterator end = capi.end();
		while (iter != end)
		{
			str.append (iter->get_xml_string ( 2 ) );
			iter++;
		}
		str.append ( Glib::ustring ( "\t</capi>\n" ) );
	}
	if ( acconto > 0 )
		str.append ( Glib::ustring ( "\t<acconto>" ) + Prestazione::get_valuta_string ( acconto ) + Glib::ustring ( "</acconto>\n" ) );
	data_ora = localtime ( &data_ritiro );
	str.append ( Glib::ustring ( "\t<data_ritiro>" + Glib::ustring::compose ( "%1-%2-%3", Glib::ustring::format ( setfill(L'0'), setw(4), data_ora->tm_year + 1900 ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mon + 1 ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_mday ) ) + "</data_ritiro>\n" ) );
	str.append ( Glib::ustring ( "\t<ora_ritiro>" + Glib::ustring::compose ( "%1:%2:%3", Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_hour ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_min ), Glib::ustring::format ( setfill(L'0'), setw(2), data_ora->tm_sec ) ) + "</ora_ritiro>\n" ) );
	if ( urgente )
		str.append ( Glib::ustring ( "\t<urgente>true</urgente>\n" ) );
	if ( consegnato )
		str.append ( Glib::ustring ( "\t<consegnato>true</consegnato>\n" ) );
	str.append ( Glib::ustring ( "</ricevuta>\n" ) );
	return str;
}

inline Glib::ustring Ricevuta::
get_ricevuta_string ( bool stampa_numero ) const
{
	int num_prest = 0;
	list<Capo>::const_iterator iter = capi.begin();
	list<Capo>::const_iterator end = capi.end();
	Glib::ustring str, str_start, str_end;
	if ( stampa_numero )
		str += "<tt><b>" + get_numero_string() + "</b> di " + Ricevuta::get_string_data(data_accettazione, STAMPA_FORMAT) + "\n";
	else
		str += "<tt>" + Ricevuta::get_string_data(data_accettazione, STAMPA_FORMAT) + "\n";
	str += "<b>" + msu::get_xml_friendly_string(nome_cliente) + "</b>\n\n";
	while ( iter != end )
	{
		const list<Prestazione>* prestazioni = iter->get_const_prestazioni ();
		list<Prestazione>::const_iterator iter_prst = prestazioni->begin();
		list<Prestazione>::const_iterator end_prst = prestazioni->end();
		Glib::ustring capo = iter_prst->get_capo ();
		if ( capo.size() > LINE_COLUMNS - 1)
				capo.assign(capo.substr(0,LINE_COLUMNS - 2) + ".");
		str += msu::get_xml_friendly_string (capo + "\n");
		while (iter_prst != end_prst)
		{			
			Glib::ustring descr = iter_prst->get_descrizione ();
			if ( descr.size() > 20 )
				descr.assign(descr.substr(0,20));
			str_end = "€ " + Prestazione::get_valuta_string(iter_prst->get_prezzo ());
			str_start = descr;
			str += msu::get_xml_friendly_string(get_formatted_line (str_start, str_end, LINE_COLUMNS));
			num_prest++;
			iter_prst++;
		}
		iter++;
	}
	str += "\n";
	str_start = Glib::ustring::format(num_prest) + " capi, totale:";
	str_end = "€ " + Prestazione::get_valuta_string(get_totale());
	str += get_formatted_line (str_start, str_end, LINE_COLUMNS);
	str_start = "Totale da pagare:";
	str_end = "€ " + Prestazione::get_valuta_string(get_totale_da_pagare ());
	str += get_formatted_line (str_start, str_end, LINE_COLUMNS);
	str += "\n<b>Ritiro capi: " + Ricevuta::get_string_data(data_ritiro, STAMPA_FORMAT) + "</b>\n\n</tt>";
	return str;
}

inline Glib::ustring Ricevuta::
get_etichetta_string ( list<Capo>::const_iterator iter, bool stampa_numero ) const
{
	Glib::ustring str;
	str += "<tt>\n";
	if ( stampa_numero )
		str += msu::get_xml_friendly_string(get_formatted_line (get_numero_string(), nome_cliente, LINE_COLUMNS));
	else
		str += msu::get_xml_friendly_string(nome_cliente + "\n");
	Glib::ustring capo = iter->get_capo();
	if ( capo.size() > LINE_COLUMNS - 1)
		capo.assign(capo.substr(0, LINE_COLUMNS - 2) + ".");
	str += msu::get_xml_friendly_string (capo + "\n");
	const list<Prestazione>* prestazioni = iter->get_const_prestazioni();
	list<Prestazione>::const_iterator iter_p = prestazioni->begin();
	list<Prestazione>::const_iterator end_p = prestazioni->end();
	while (iter_p != end_p)
	{
		Glib::ustring descrizione = iter_p->get_descrizione ();
		if ( descrizione.size() > 20 )
			descrizione.assign(descrizione.substr(0, 20));
		Glib::ustring prezzo_str = "€ " + Prestazione::get_valuta_string(iter_p->get_prezzo());
		str += msu::get_xml_friendly_string(get_formatted_line (descrizione, prezzo_str, LINE_COLUMNS));
		iter_p++;
	}
	str += "</tt>";
	return str;
}

#endif // _RICEVUTA_H_

